#ifndef LOG_HPP
#define LOG_HPP
#pragma once

#include <QString>
#include <QDateTime>
#include <QDebug>
#include <QFileInfo>

namespace utils {

enum class Level {
    Debug,
    Info,
    Warn,
    Error,
    Fatal
};

inline QString getTime()
{
    return QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
}

inline QString getFileName(const QString& path)
{
    return QFileInfo(path).fileName();
}

inline QString levelToString(Level level)
{
    switch (level) {
        case Level::Debug: return "DEBUG" ;
        case Level::Info:  return "INFO"  ;
        case Level::Warn:  return "WARN"  ;
        case Level::Error: return "ERROR" ;
        case Level::Fatal: return "FATAL" ;
        default:           return "UNKOWN";
    }
}

template<typename T>
QString toString(const T& val)
{
    QString s;
    QDebug debug(&s);
    debug.noquote().nospace() << val;
    return s;
}

inline void formatImpl(QString& result, const QString& fmt) { result += fmt; }

template<typename T, typename... Args>
void formatImpl(QString& result, const QString& fmt, const T& val, Args&&... args)
{
    int idx = fmt.indexOf("{}");
    if (idx == -1) {
        result += fmt;
        return;
    }

    result += fmt.left(idx);
    result += toString(val);
    formatImpl(result, fmt.mid(idx + 2), std::forward<Args>(args)...);
}

template<typename... Args>
QString format(const QString& fmt, Args&&... args)
{
    QString res;
    formatImpl(res, fmt, std::forward<Args>(args)...);
    return res;
}

template<typename... Args>
void log(Level level, const char* file, int line, const QString& fmt, Args&&... args)
{
    QString msg = format(fmt, std::forward<Args>(args)...);
    QString lineStr = QString("[%1][%2:%3][%4] %5").arg(
        getTime(), getFileName(file), QString::number(line), levelToString(level), msg
    );
    qDebug().noquote() << lineStr;
}

} // namespace utils

#define LOG(level, fmt, ...) utils::log(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__)

#define LL_DEBUG utils::Level::Debug
#define LL_INFO utils::Level::Info
#define LL_WARN utils::Level::Warn
#define LL_ERROR utils::Level::Error
#define LL_FATAL utils::Level::Fatal


#endif // LOG_HPP
